package com.gframework.mybatis.service.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import com.gframework.mybatis.dao.IViewDao;
import com.gframework.mybatis.service.IService;
import com.gframework.mybatis.service.ITableService;
import com.gframework.mybatis.service.IViewService;
import com.gframework.sqlparam.SqlParam;
import com.gframework.util.ReflectUtils;

/**
 * IViewService 公共接口基本实现类
 * 
 * @since 2.0.0
 * @author Ghwolf
 * @see ITableService
 * @see IViewService
 * @see IService
 * @see TableServiceImpl
 * @see ServiceImpl
 */
public class ViewServiceImpl<M extends IViewDao<T>, T extends Serializable> implements IViewService<M, T> {

	protected final Logger logger = LoggerFactory.getLogger(getClass());

	private static final List<String> DEFAULT_METHOD_NAMES;
	static {
		List<String> names = new ArrayList<>();
		names.add("list");
		names.add("getOne");
		names.add("getBigInteger");
		names.add("getBigDecimal");
		names.add("getDate");
		names.add("getDouble");
		names.add("getInt");
		names.add("getLong");
		names.add("getString");
		DEFAULT_METHOD_NAMES = Collections.unmodifiableList(new ArrayList<>(names));
	}

	/**
	 * dao类对象，由spring进行诸如
	 */
	@Autowired
	protected M dao;
	/**
	 * dao接口类型
	 */
	protected final Class<M> daoType;
	/**
	 * pojo实体类类型
	 */
	protected final Class<T> pojoType;
	/**
	 * sqlSessionFactory类对象
	 */
	@Autowired
	protected SqlSessionFactory sqlSessionFactory;

	@SuppressWarnings("unchecked")
	public ViewServiceImpl() {

		// init dao type

		Class<?> serviceType = getClass();
		if (AopUtils.isAopProxy(serviceType)) {
			serviceType = serviceType.getSuperclass();
		}
		Class<?> genericType = ReflectUtils.getParameterizedClass(serviceType, IViewService.class, 0);
		if (genericType == null || genericType == Object.class) {
			throw new ServiceImplException(
					"ServiceImpl初始化异常！请为 " + serviceType.getName() + " 类的ServiceImpl父类设置dao泛型类型！");
		}
		this.daoType = (Class<M>) genericType;

		// init pojo type

		Class<?> genericType2 = ReflectUtils.getParameterizedClass(serviceType, IViewService.class, 1);
		if (genericType2 == null || genericType2 == Object.class) {
			throw new ServiceImplException(
					"ServiceImpl初始化异常！请为 " + serviceType.getName() + " 类的ServiceImpl父类设置实体类泛型类型！");
		}
		this.pojoType = (Class<T>) genericType2;
	}

	/**
	 * 获取一个用于批处理的SqlSession类对象.
	 * <p>
	 * 需要注意的是，使用这个方法获取到SqlSession后，则需要手动对SqlSession进行关闭
	 */
	protected SqlSession getSqlSessionBatch() {
		return sqlSessionFactory.openSession(ExecutorType.BATCH);
	}

	/**
	 * 获取当前service映射的dao方法的statementId.
	 * <p>
	 * statementId可用于{@link SqlSession}类方法的调用
	 * <p>
	 * statementId的格式通常为："dao类全名.方法名称"
	 * <p>
	 * <strong>不支持内置dao中的default方法，因为他不属于mybatis的statement</strong>
	 * 
	 * @param methodName dao方法名称
	 * @return 返回一个statementId，一定不是null
	 * @throws IllegalArgumentException 如果methodName为null
	 * @throws UnsupportedOperationException
	 *             如果要获取内置的default方法名称的statementId，则抛出此异常
	 */
	protected String getStatementId(String methodName) {
		Assert.notNull(methodName, "methodName 不能为null！");
		if (DEFAULT_METHOD_NAMES.contains(methodName)) {
			throw new UnsupportedOperationException("不能获取methodName=" + methodName + " 的statementId，因为这是一个default方法！");
		}
		return daoType.getName() + '.' + methodName;
	}

	@Override
	public M getDao() {
		return dao ;
	}
	
	/**
	 * 获取mybatis配置类对象
	 */
	public Configuration getConfiguration(){
		return sqlSessionFactory.getConfiguration();
	}

	@Override
	public List<T> listAll() {
		return getDao().findAll();
	}

	@Override
	public List<T> list(SqlParam sqlParam) {
		return getDao().find(sqlParam);
	}
	
	@Override
	public <R> List<R> list(SqlParam sqlParam, Class<R> returnType) {
		return getDao().list(sqlParam,returnType);
	}

	@Override
	public long count() {
		return getDao().count(null);
	}

	@Override
	public long count(SqlParam sqlParam) {
		return getDao().count(sqlParam);
	}


}
